'''
Created on Jul 9, 2011

@author: kafkef
'''

from PyQt4.QtGui import *
from PyQt4.QtCore import Qt, QModelIndex, QVariant, SIGNAL, QAbstractTableModel


from dexen.system.client import ui, db
from dexen.system.common.base import Action
from dexen.system.common.transact import RecvTransact
from dexen.system.client.ui.dialogs.output_dlg import OutputDlg
from dexen.system.client.ui.dialogs.export_dlg import ExportDialog
from dexen.system.client.ui.widgets.table_view import TableView


svr_proxy = ui.svr_proxy


#===============================================================================
# Classes
#===============================================================================
class JobLogModel(QAbstractTableModel):
    def __init__(self, proxy_model, parent = None):
        super(JobLogModel, self).__init__(parent)
        self._header_items = []
        self._content_items = []
        self._content_str = ""        
        self._actions = []
        self.proxy_model = proxy_model
    
    def rowCount(self, index=QModelIndex()):
        return len(self._content_items)
    
    def columnCount(self, index=QModelIndex()):
        if len(self._content_items) == 0:
            return 0
        return len(self._content_items[0])

    def headerData(self, section, orientation, role=Qt.DisplayRole):
        if role != Qt.DisplayRole or self._header_items == []:
            return QVariant()
        if orientation == Qt.Horizontal:
            return QVariant(self._header_items[section])
        if orientation == Qt.Vertical:
            return QVariant(section + 1)

    def data(self, index, role=Qt.DisplayRole):
        assert isinstance(index, QModelIndex)
        if not index.isValid(): return QVariant()
        
        if role == Qt.BackgroundRole:
            action = self._actions[index.row()]
            assert isinstance(action, Action)
            if action.is_master():
                return QColor(Qt.blue)
            if action.is_receive():
                if self._task_failed(action):
                    return QColor(Qt.red)
                return QColor(0x4E, 0x92, 0x58, 128)
            return QColor(Qt.gray)
        
        if role == Qt.ForegroundRole:
            action = self._actions[index.row()]
            assert isinstance(action, Action)
            if action.is_master() or self._task_failed(action):
                return QColor(Qt.white)
            return QColor(Qt.black)
         
        if role == Qt.DisplayRole:
            return QVariant(self._content_items[index.row()][index.column()])

    def get_log(self):
        return self._content_str   

    def set_proxy_model(self, proxy_model):
        self.proxy_model = proxy_model  
    
    def get_proxy_model(self):
        return self.proxy_model
                
    def add_actions(self, actions):
        if len(actions) == 0:
            return
        
        if len(self._header_items) == 0:
            self._set_header(actions[-1])
            
        self._actions.extend(actions)
        
        action_items = []
        action_logs = []
        for action in actions:
            assert isinstance(action, Action)
            log_str = action.get_log()
            log_items = log_str.split(",")
            action_items.append(log_items)
            action_logs.append(log_str)
            
        self._add_logs(action_items, action_logs)
    
    def _set_header(self, action):
        assert isinstance(action, Action)
        header = action.get_header()
        self._header_items = header.split(",")
        self._header_tooltips = action.get_header_tooltip().split(",")
        
        assert self.columnCount() == 0
        self.beginInsertColumns(QModelIndex(), 0, len(self._header_items) - 1)
        self.endInsertColumns()        
        
        self._content_str = header
    
    def _add_logs(self, content_items, content_logs):
        cur_pos = len(self._content_items)
        n_rows = len(content_items)
        
        if n_rows == 0: return
        
        self.beginInsertRows(QModelIndex(), cur_pos, cur_pos + n_rows - 1)
        self._content_items.extend(content_items)
        self.endInsertRows()
                
        content_str = "\n".join(content_logs)
        self._content_str += "\n" + content_str

    def _task_failed(self, action):
        assert isinstance(action, Action)
        if not action.is_receive():
            return False        
        transact = action.get_transact()
        assert isinstance(transact, RecvTransact)
        return transact.has_task_failed()

    def get_action(self, index):
        assert isinstance(index, QModelIndex)
        return self._actions[index.row()]


class SortFilterProxyModel(QSortFilterProxyModel):
    def __init__(self, parent=None):
        QSortFilterProxyModel.__init__(self, parent)
    
    def lessThan(self, left, right):
        assert isinstance(left, QModelIndex)
        assert isinstance(right, QModelIndex)
        
        leftData = self.sourceModel().data(left)
        rightData = self.sourceModel().data(right)
        
        assert isinstance(leftData, QVariant)
        assert isinstance(rightData, QVariant)
        
        if left.column() == 0 or left.column() == 1:
            return leftData.toInt() < rightData.toInt()
        
        return leftData.toString() < rightData.toString()

    def headerData(self, section, orientation, role=Qt.DisplayRole):
        if orientation == Qt.Horizontal and role == Qt.ToolTipRole:
            model = self.sourceModel()
            assert isinstance(model, JobLogModel)
            return QVariant(model._header_tooltips[section])
            
        if role != Qt.DisplayRole:
            return QVariant()
        if orientation == Qt.Horizontal:
            return self.sourceModel().headerData(section, orientation, role)
        if orientation == Qt.Vertical:
            return QVariant(section + 1)

    def get_content(self):
        model = self.sourceModel()
        assert isinstance(model, JobLogModel)
        content = ",".join(model._header_items) + "\n"
        for i in xrange(self.rowCount()):
            line = ""
            cols = self.columnCount()
            for j in xrange(cols - 1):
                variant = self.data(self.index(i,j))
                assert isinstance(variant, QVariant)
                line += str(variant.toString()) + ","
            line += str(self.data(self.index(i, cols-1)).toString())
            content += line + "\n"
        return content        


class ExecutionLogPage(QWidget):       
    def __init__(self):
        super(ExecutionLogPage, self).__init__()
        self.curJobInfo = None
        self.models = {} #job_id: BaseTableModel
        self._setupWidgets()

    def _setupWidgets(self):
        # Create widgets
        self.tableView = TableView()
        self.exportBtn = QPushButton("Export")
        
        # Configure widgets
        self.tableView.resizeColumnsToContents()
        
        # Layout widgets
        hLayout = QHBoxLayout()
        hLayout.addStretch()
        hLayout.addWidget(self.exportBtn)
        layout = QVBoxLayout()       
        layout.addLayout(hLayout)
        layout.addWidget(self.tableView)
        self.setLayout(layout)
        
        # Configure signal/slot connections
        self.connect(self.exportBtn, SIGNAL("clicked()"), self.on_exportBtn_clicked)
        self.connect(self.tableView, SIGNAL("doubleClicked(const QModelIndex&)"),
                     self.on_doubleClicked)

    def get_model(self, job_info):
        assert isinstance(job_info, db.JobInfo)
        if not self.models.has_key(job_info):
            self._construct_model(job_info)
        assert self.models.has_key(job_info)
        return self.models[job_info]
    
    def _construct_model(self, job_info):
        proxy_model = SortFilterProxyModel()
        proxy_model.setDynamicSortFilter(True)
        self.models[job_info] = JobLogModel(proxy_model)
        proxy_model.setSourceModel(self.models[job_info])
    
    def get_proxy_model(self, job_info):
        model = self.get_model(job_info)
        assert isinstance(model, JobLogModel)
        return model.get_proxy_model()
                
    def on_doubleClicked(self, index):
        assert isinstance(index, QModelIndex)
        proxy_model = self.get_proxy_model(self.curJobInfo)        
        model = self.get_model(self.curJobInfo)

        assert isinstance(proxy_model, SortFilterProxyModel)
        assert isinstance(model, JobLogModel)

        print "row, column", index.row(), " ", index.column()
        index = proxy_model.mapToSource(index)
        action = model.get_action(index)
        assert isinstance(action, Action)
        dlg = OutputDlg(action, self)
        dlg.exec_()
        
    def on_cur_job_changed(self, job_info):            
        self.curJobInfo = job_info
        proxy_model = self.get_proxy_model(job_info) 
        self.tableView.setModel(proxy_model)
        
    def on_actions_received(self, job_info, actions):
        print "ExecutionLogPage:on_actions_received"
        print len(actions), "are received"

        model = self.get_model(job_info)
        assert isinstance(model, JobLogModel)
        model.add_actions(actions)
        
        # Update the view based on the new contents
        self.tableView.resizeColumnsToContents() 

    def on_actions_initialized(self, job_info, actions):
        self.on_actions_received(job_info, actions)
    
    def on_exportBtn_clicked(self):
        model = self.tableView.model()
        assert isinstance(model, SortFilterProxyModel)
        content = model.get_content()
        dlg = ExportDialog(self)
        ret_val = dlg.exec_()
        if ret_val == QDialog.Accepted:
            file_path = dlg.get_file_path()
            f = open(file_path, "wb")
            f.write(content)
            f.close()


        